﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO;
using System.Reflection;

namespace Microsoft.Reputation.Tests.FrontEndCommon
{
    public enum TaxonomyLevel
    {
        Category,
        Rollup,
        Type
    }

    public class TaxonomyCategoryInfo
    {
        private uint id;
        private string name;
        private string description;
        private TaxonomyLevel taxonomyLevel;
        private TaxonomyCategoryInfo parent;
        private List<TaxonomyCategoryInfo> children;

        public TaxonomyCategoryInfo(
            uint id,
            string name,
            string description,
            TaxonomyLevel taxonomyLevel,
            TaxonomyCategoryInfo parent)
        {
            this.id = id;
            this.name = name;
            this.description = description;
            this.taxonomyLevel = taxonomyLevel;
            this.parent = parent;
            this.children = new List<TaxonomyCategoryInfo>();
        }

        public List<TaxonomyCategoryInfo> Children
        {
            get
            {
                return children;
            }
        }

        public TaxonomyCategoryInfo Parent
        {
            get
            {
                return parent;
            }
        }

        public TaxonomyLevel TaxonomyLevel
        {
            get
            {
                return taxonomyLevel;
            }
        }

        public uint Id 
        {
            get
            {
                return id;
            }
        }

        public string Name
        {
            get
            {
                return name;
            }
        }

        public string Description
        {
            get
            {
                return description;
            }
        }
    }

    public static class Taxonomy
    {
        private const string CategoryXmlFileName = "Cats.xml";

        private static Dictionary<uint, TaxonomyCategoryInfo> categories = LoadCategoryData();

        public static void Initialize()
        {
        }

        public static List<TaxonomyCategoryInfo> GetTypes()
        {
            return categories.Values.Where(
                catInfo => catInfo.TaxonomyLevel == TaxonomyLevel.Type).ToList();
        }

        public static List<TaxonomyCategoryInfo> GetRollups()
        {
            return categories.Values.Where(
                catInfo => catInfo.TaxonomyLevel == TaxonomyLevel.Rollup).ToList();
        }

        public static List<TaxonomyCategoryInfo> GetAllCategories()
        {
            return categories.Values.ToList();
        }

        /// <summary>
        /// Returns all categories where ID, Name, or Description contain the search text (case insensitive)
        /// </summary>
        /// <param name="searchText">Text to search for</param>
        /// <returns></returns>
        public static List<TaxonomyCategoryInfo> SearchForCategory(string searchText)
        {
            searchText = searchText.ToLower();
            List<TaxonomyCategoryInfo> categoryList = GetAllCategories();
            return categoryList.Where(
                catInfo =>
                    catInfo.Id.ToString().Contains(searchText) ||
                    catInfo.Name.ToLower().Contains(searchText) ||
                    catInfo.Description.ToLower().Contains(searchText)).ToList();
        }

        public static TaxonomyCategoryInfo GetCategory(uint id)
        {
            TaxonomyCategoryInfo result;
            categories.TryGetValue(id, out result);
            if (result == null)
            {
                throw new ApplicationException("The category " + id + " does not exist");
            }

            return result;
        }

        private static TaxonomyCategoryInfo CreateCategoryInfo(
            TaxonomyLevel level, 
            XElement e,
            TaxonomyCategoryInfo parent)
        {
            return new TaxonomyCategoryInfo(
                Convert.ToUInt32(e.Attribute("_locID").Value),
                e.Attribute("Name").Value,
                e.Attribute("Description").Value,
                level,
                parent);
        }

        private static string GetAssemblyDirectory()
        {
            Uri uri = new Uri(Assembly.GetExecutingAssembly().CodeBase);
            string assemblyFullPath = uri.LocalPath;
            string assemblyDirectory = Path.GetDirectoryName(assemblyFullPath);

            return assemblyDirectory;
        }

        public static bool DesignMode
        {

            get
            {
                return (System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv");
            }
        }

        private static Dictionary<uint, TaxonomyCategoryInfo> LoadCategoryData()
        {
            Dictionary<uint, TaxonomyCategoryInfo> result = new Dictionary<uint, TaxonomyCategoryInfo>();
            if (DesignMode)
            {
                return result;
            }
            
            string catsFile = Path.Combine(GetAssemblyDirectory(), CategoryXmlFileName);

            XDocument xml = XDocument.Load(catsFile);

            foreach (XElement type in xml.Descendants("CategoryType"))
            {
                TaxonomyCategoryInfo typeCategoryInfo = CreateCategoryInfo(
                    TaxonomyLevel.Type, 
                    type, 
                    null);
                
                result.Add(typeCategoryInfo.Id, typeCategoryInfo);

                foreach (XElement rollup in type.Descendants("CategoryRollup"))
                {
                    TaxonomyCategoryInfo rollupCategoryInfo = CreateCategoryInfo(
                        TaxonomyLevel.Rollup, 
                        rollup, 
                        typeCategoryInfo);
                    
                    result.Add(rollupCategoryInfo.Id, rollupCategoryInfo);
                    typeCategoryInfo.Children.Add(rollupCategoryInfo);

                    foreach (XElement category in rollup.Descendants("Category"))
                    {
                        TaxonomyCategoryInfo categoryCategoryInfo = CreateCategoryInfo(
                            TaxonomyLevel.Category, 
                            category, 
                            rollupCategoryInfo);

                        result.Add(categoryCategoryInfo.Id, categoryCategoryInfo);
                        rollupCategoryInfo.Children.Add(categoryCategoryInfo);
                    }
                }
            }

            return result;
        }
    }
}
